import { _decorator, Component, Size, tween, Tween, UITransform, v3, Vec2, Vec3 } from 'cc';
import { Tile } from './tile';
const { ccclass, property } = _decorator;

@ccclass('Drawer')
export class Drawer extends Component {

	protected uitrans: UITransform;

	/** 抽屉的容量 */
	protected capacity: number;
	/** 基准线高度 */
	protected base_height: number;
	/** 抽屉中的砖块尺寸 */
	protected tile_size: Size;
	/** 容器的左边界 */
	protected left_board: number;
	/** 砖块之间的间距 */
	protected padding_x: number = 0;
	/** tween动画的速度 */
	protected tween_speed: number;

	/** 正在添加到抽屉中的砖块(动画中) */
	protected adding_tile: {token: string, np: mtec.NudityPromise<string>}[] = [];
	/** 当前容器内的砖块数据 */
	protected record_list: {tier: number, coor: Vec2, tile: Tile, token?: string}[] = [];
	/** 当前容器内的砖块 */
	protected tile_list: Tile[] = [];

	/** 剩余空间 */
	public get residue(){
		return this.capacity - this.record_list.length;
	}

	public get tileCount(){
		return this.tile_list.length;
	}

	protected onLoad(){
		this.uitrans = this.node.getComponent(UITransform);
		this.record_list = [];
		this.adding_tile = [];
		this.tile_list = [];
	}

    protected start(){
		this.left_board = -this.uitrans.width * this.uitrans.anchorX;

		this.tile_size = Tile.calculateViewSize(this.uitrans.width / this.capacity, this.uitrans.height).box;
		this.padding_x = (this.uitrans.width - this.tile_size.width * this.capacity) / (this.capacity + 1);
		this.tween_speed = Math.max(this.tile_size.width, this.tile_size.height) / 0.1;
    }

	protected recordToken(record: typeof this.record_list[number]){
		record.token = [
			['id', record.tile.ID],
			['tier', record.tier],
			['x', record.coor.x],
			['y', record.coor.y]
		].map(el=>el.join(':')).join('; ');

		return record.token;
	}

	protected getPostionByIndex(index: number, out?: Vec3){
		let x = this.left_board + this.tile_size.width * (index + 0.5) + this.padding_x * (index + 1);
		let y = this.base_height - (this.uitrans.anchorY - 0.5) * this.uitrans.height;
		if(out) out.set(x, y);
		else out = v3(x, y);

		return out;
	}

	protected getNext(tile: Tile, out?: Vec3){
		if(!this.base_height){
			this.base_height = tile.convertToViewSpaceAR(Vec3.ZERO).y;
			this.base_height = mtec.number.fixedNum(this.base_height, 4);
		}
		let index = this.record_list.length;
		return {index, position: this.getPostionByIndex(index, out)};
	}

	private refresh_distance = v3();
	protected refresh(){
		let np = new mtec.NudityPromise<'end'>();

		let duration = 0;

		let distance = 0;
		let time = 0;
		this.tile_list.forEach((tile, index)=>{
			let token = this.record_list.find(el=>el.tile==tile).token;
			if(this.adding_tile.find(el=>el.token==token)) return void 0;

			let position = this.getPostionByIndex(index);
			distance = Vec3.subtract(this.refresh_distance, tile.node.position, position).length();
			distance = Math.round(distance * 10000) / 10000;

			if(!(distance>0)) return void 0;

			time = distance / this.tween_speed;
			if(time > duration) duration = time;

			Tween.stopAllByTarget(tile.node);
			tween(tile.node).to(time, {position}, {easing: 'cubicOut'}).set({position}).start();
		});

		this.scheduleOnce(()=>np.resolve('end'), duration + 0.1);
		return np.promise;
	}

	protected removeRecordByTile(tile: Tile){
		let record = mtec.array.remove(this.record_list, el=>el.tile==tile);
		if(record){
			mtec.array.remove(this.tile_list, tile);
			mtec.array.remove(this.adding_tile, el=>el.token==record.token);
		}

		return record;
	}

	protected afterPadded(record: typeof this.record_list[number]){

	}

	public padding(record: {tier: number, coor: Vec2, tile: Tile}, world_position?: Vec3){
		let next = this.getNext(record.tile);
		let token = this.recordToken(record);
		if(this.record_list.find(el=>el.token==token)) return void 0;

		let np = new mtec.NudityPromise<string>();
		this.adding_tile.push({token, np});
		this.record_list.push(record);
		this.tile_list.splice(next.index, 0, record.tile);
		if(world_position) this.uitrans.convertToWorldSpaceAR(next.position, world_position);
		else world_position = this.uitrans.convertToWorldSpaceAR(next.position);
		record.tile.setSize(this.tile_size);
		this.refresh();
		return {
			token,
			world_position,
			promise: np.promise,
		};
	}

	public padded(token: string){
		let info = mtec.array.remove(this.adding_tile, el=>el.token==token);
		if(info){
			info.np.resolve(token);
		}

		let record = this.record_list.find(el=>el.token==token);
		if(record){
			let position = this.uitrans.convertToNodeSpaceAR(record.tile.node.worldPosition);
			record.tile.node.removeFromParent();
			record.tile.node.setParent(this.node);
			record.tile.node.setPosition(position);
		}

		this.afterPadded(record);
	}

	private clean_call_list: Array<(list: Tile[])=>void> = [];
	public clean(){
		mtec.array.clear(this.adding_tile);
		mtec.array.clear(this.record_list);
		let tile_list = mtec.array.clear(this.tile_list);
		this.clean_call_list.forEach(fn=>fn(tile_list));
	}

	public onClean(fn: (list: Tile[])=>void){
		this.clean_call_list.push(fn);
	}
}

